<?php

class MissionService extends AbstractService
{
    /**
     * @var MissionService
     */
    private static $instance;

    /**
     * Get the instance of MissionService.
     *
     * @return MissionService
     */
    public static function get()
    {
        if (!self::$instance) {
            self::$instance = new MissionService();
        }
        return self::$instance;
    }

    /**
     * Construction.
     */
    protected function __construct()
    {
        parent::__construct();
    }

    /**
     * 玩家创建时触发的逻辑
     *
     * @param int $userId
     * @return void
     */
    public function handleCreate($userId)
    {
        // do nothing
    }

    /**
     * 玩家登录时触发逻辑
     *
     * @param int $userId
     * @return void
     */
    public function handleLogin($userId)
    {
        AchievementMissionService::get()->handleLogin($userId);
    }

    /**
     * 提供当前 service 数据
     *
     * @param int $userId
     * @param boolean $visitMode default false
     * @return array(MineVoList)
     */
    public function info($userId, $visitMode = false)
    {
        $missionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_MISSION);
        return array(
            $missionList
        );
    }

    /**
     * 接取新任务 (不处理后续任务逻辑,只判定是否有重复任务)
     *
     * @param int $userId 玩家Id
     * @param int $missionId 任务id
     * @param int $reqTime 请求时间
     * @param int $startTime 差值时间，用来更新结束时间和开始时间
     * @param int $endTime 差值时间，用来更新结束时间和开始时间（def：SQL_INT_LIMITED 视为无限时间）
     * @param int $initialValue 初始进度，用来设置条件的初始进度，主要用于初始值不为0的条件，例如成就
     * @param boolean $canReFlush 是否允许重新刷新任务时间
     * @param MissionVoList $missionList
     * @return MissionVoList
     * @throws SzException
     */
    public function accept($userId, $missionId, $reqTime, $startTime = 0, $endTime = GameConst::SQL_INT_LIMITED, $initialValue = 0, $missionList = null, $canReFlush = false)
    {
        if (!$missionId) {
            return null;
        }

        $taskSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_TASK, $missionId, true);

        if (!$missionList) {
            $missionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_MISSION);
        }

        // 已经接收过该任务
        /** @var MissionVo $mission */
        $mission = $missionList->getElement($missionId, false);
        if ($mission && !$canReFlush) {
            return null;
        }

        // 任务条件
        $conditionIds = array();
        foreach ($taskSetting['conditionId'] as $conditionId) {
            $conditionIds[$conditionId] = $initialValue;
        }

        if ($mission && $canReFlush) {
            // 处理任务已存在，并且允许刷新任务
            $mission->setConditionsProgress($conditionIds);
            $mission->setDrop($taskSetting['basicRewards']);
            $mission->setMissionUpdateTime($reqTime);
            $mission->setMissionStartTime($startTime);
            $mission->setMissionEndTime($endTime);
            $missionList->updateElement($missionId, $mission);
        } else {
            // 创建任务
            $mission = new MissionVo(
                $userId,
                $missionId,
                json_encode($conditionIds),
                json_encode($taskSetting['basicRewards']),
                $reqTime,
                $startTime,
                $endTime,
                true
            );
            $missionList->addElement($mission);
            $missionList->setElement($missionId, $mission);
        }
        SzPersister::get()->setVoList($missionList);

        // 更新任务记录
        $missions = UserService::get()->getInfo($userId, GameConst::USER_INFO_TYPE_MISSIONS);
        $missions = (!$missions) ? array() : $missions;
        $missions[] = $missionId;
        UserService::get()->updateProfileInfo($userId, GameConst::USER_INFO_TYPE_MISSIONS, $missions);

        return $missionList;
    }

    /**
     * 删除任务
     *
     * @param $userId
     * @param boolean $errNotFound whether to throw exception when element not found
     * @param MissionVoList $missionList
     * @param $missionId
     */
    public function remove($userId, $missionId, $errNotFound = true, $missionList = null)
    {
        if (!$missionList) {
            $missionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_MISSION);
        }

        // 从任务列表中删除
        $missionList->deleteElement($missionId, $errNotFound);
        SzPersister::get()->setVoList($missionList);

        // 从进行中的任务中删除
        $missions = UserService::get()->getInfo($userId, GameConst::USER_INFO_TYPE_MISSIONS);
        $missions = (!$missions) ? array() : $missions;
        $offset = array_search($missionId, $missions);

        if ($offset !== false) {
            array_splice($missions, $offset, 1);
            UserService::get()->updateProfileInfo($userId, GameConst::USER_INFO_TYPE_MISSIONS, $missions);
        }
    }


    /**
     * 任务处理的统一入口，根据 entityId 匹配任务，并更新任务进度
     *
     * @param int $userId
     * @param int $action action id
     * @param int $module functional module id
     * @param int $subId detailed sub id under some module
     * @param int $progressCount
     * @return void
     */
    public function progress($userId, $action, $module, $subId, $progressCount)
    {
        // 验证功能是否解锁
        if (!GameService::get()->verifySysUnlock($userId, GameConst::FUNC_MISSION_FUNC, true)) {
            return;
        }

        // 获取当前任务列表
        $missions = UserService::get()->getInfo($userId, GameConst::USER_INFO_TYPE_MISSIONS);
        $missions = (!$missions) ? array() : $missions;
        if (empty($missions)) {
            return;
        }

        // 拼接当前任务行为id
        $contentEntityId = EntityService::get()->buildEntityId($action, $module, $subId);

        $taskSettings = SzConfig::get()->loadAppConfig(GameConst::CONFIG_TASK, null, true);
        $taskConditionSettings = SzConfig::get()->loadAppConfig(GameConst::CONFIG_TASK_CONDITION, null, true);

        // 遍历任务列表
        $needUpdateMission = [];
        foreach ($missions as $missionId) {
            $conditions = [];

            // 遍历任务条件（EntityId）
            if (!SzUtility::checkArrayKey($missionId, $taskSettings)) {
                continue;
            }

            foreach ($taskSettings[$missionId]['conditionId'] as $targetConditionId) {
                if (!SzUtility::checkArrayKey($targetConditionId, $taskConditionSettings)) {
                    continue;
                }
                $taskConditionSetting = $taskConditionSettings[$targetConditionId];

                if ($contentEntityId == $taskConditionSetting['content'] && !in_array($targetConditionId, $conditions)) {
                    // 任务条件完全匹配，更新任务
                    $conditions[] = $targetConditionId;
                } else {
                    // 任务条件不匹配，判断任务条件的末8位是否是 99999999, 并且 action，module 与当前任务的 action，module 相同。
                    list($targetAction, $targetModule, $targetSubId) = EntityService::get()->parseEntityId($taskConditionSetting['content']);
                    if ($targetSubId != GameConst::ENTITY_PADDING_NULL || $action != $targetAction || $module != $targetModule) {
                        continue;
                    }

                    // 具体规则参见任务条件类型说明文档,每个子数据只能解析到前四位,所以数据不要超过四位
                    // Eg：[4001,4002]和[40011,40021]均解析为[4001,4002]
                    foreach ($taskConditionSetting['value'] as $valueId) {
                        if ((substr($subId, 0, strlen($valueId)) == $valueId || $valueId == -1)) {
                            // subId 完全匹配 EntityValue ，更新任务
                            $conditions[] = $targetConditionId;
                        }
                    }
                }
            }

            if (count($conditions) > 0) {
                $needUpdateMission[$missionId] = $conditions;
            }
        }

        // 更新任务进度
        if (count($needUpdateMission) > 0) {
            $missionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_MISSION);

            foreach ($needUpdateMission as $missionId => $conditions) {
                foreach ($conditions as $targetConditionId) {
                    $this->updateConditionProgress($userId, $missionId, $targetConditionId, $progressCount, $missionList);
                }
            }

            SzPersister::get()->setVoList($missionList);
        }
    }

    /**
     * 更新任务进度
     *
     * @param int $userId
     * @param int $missionId
     * @param int $conditionId
     * @param int $progressCount
     * @param MissionVoList $missionList
     * @return void
     */
    private function updateConditionProgress($userId, $missionId, $conditionId, $progressCount, $missionList = null)
    {
        if (!$missionList) {
            $missionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_MISSION);
        }

        /**
         * @var MissionVo $mission
         */
        $mission = $missionList->getElement($missionId, false);
        if (!$mission) {
            return;
        }

        // 未开始统计 || 已结束统计(若为0则无视结束时间)
        $reqTime = GameService::get()->getReqTime();
        if ($reqTime < $mission->getMissionStartTime() || $reqTime > $mission->getMissionEndTime()) {
            return;
        }

        //判是否条件已达到规定目标
        $taskSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_TASK, $missionId, true);
        $taskConditionSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_TASK_CONDITION, $conditionId, true);
        if ($this->isConditionSatisfyDemand($userId, $missionId, $conditionId, $mission, $taskSetting, $taskConditionSetting)) {
            return;
        }

        $conditionProgress = $mission->getRawConditionsProgress();

        // 限时任务条件，例如任务A，必须在10秒内点击100下，如果超过10秒，则重新计数
        if ($taskConditionSetting["completeTimeLimit"] != -1
            && $reqTime >= ($mission->getMissionUpdateTime() + $taskConditionSetting['completeTimeLimit'])) {
            $mission->setMissionUpdateTime($reqTime);
            $progressCount = -$conditionProgress[$conditionId];
        }

        // 判断任务条件属于累加的，还是替换的，如果是替换的，取最大值。
        if ($taskConditionSetting['replace'] == 1) {
            $conditionProgress[$conditionId] = max($conditionProgress[$conditionId], $progressCount);
        } else {
            $conditionProgress[$conditionId] += $progressCount;
            // 数量超过任务条件
            if ($conditionProgress[$conditionId] > $taskConditionSetting['count'] && $taskSetting['taskType'] != GameConst::TASK_TYPE_ACHIEVEMENT) {
                $conditionProgress[$conditionId] = $taskConditionSetting['count'];
            }
        }

        // 任务进度更新
        $mission->setConditionsProgress($conditionProgress);
        $missionList->updateElement($missionId, $mission);
    }


    /**
     * 完成当前任务并可选下发后续任务
     *
     * @param int $userId
     * @param int $missionId
     * @param boolean $clearMissionInfo 是否从 profileInfo 中删除任务
     * @param int $initialValue
     * @param MissionVoList $missionList
     * @param string $logItem
     * @throws SzException
     * @return MissionVoList
     */
    public function finish($userId, $missionId, $clearMissionInfo = true, $initialValue = 0, $missionList = null, $logItem = "")
    {
        $reqTime = GameService::get()->getReqTime();
        $taskSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_TASK, $missionId, true);

        if (!$missionList) {
            $missionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_MISSION);
        }

        /**
         * @var MissionVo $mission
         */
        $mission = $missionList->getElement($missionId);

        // 任务条件验证
        list($isSatisfy, $notSatisfyConditionIds, $consumeItems) = $this->isSatisfyDemand($userId, $missionId, $mission);
        if (!$isSatisfy) {
            throw new SzException(21102, array($missionId, json_encode($notSatisfyConditionIds)));
        }

        // 扣除任务物品
        if (!empty($consumeItemDefIds)) {
            ItemService::get()->processItems($userId, $consumeItems);
        }

        // 任务完成，删除任务
        if ($clearMissionInfo) {
            MissionService::get()->remove($userId, $missionId, true, $missionList);
        }

        // 获得任务掉落
        $outputTid = '';
        foreach ($mission->getRawDrop() as $entityId => $count) {
            GameService::get()->handleEntity($userId, $entityId, $count, true, true); //经确认可超上限
            $outputTid .= $entityId . ':' . $count . '|';
        }

        // 接取后置任务
        foreach ($taskSetting['nextTask'] as $nextTaskId) {
            if ($nextTaskId !== -1) {
                MissionService::get()->accept($userId, $nextTaskId, $reqTime, $reqTime, GameConst::SQL_INT_LIMITED, $initialValue);
            }
        }

        // 数据采集-成就任务
        if ($taskSetting['taskType'] == GameConst::TASK_TYPE_ACHIEVEMENT) {
            // 数据采集：成就奖励领取
            StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_USER_ORBIT, array(
                'clientData' => array(
                    'tid' => 'Module:Achievement|action:claim|',
                    'value' => 1
                )
            ));

            // 数据采集：成就奖励产出
            StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_RESOURCE_OUTPUT, array(
                'clientData' => array(
                    'tid' => $outputTid,
                    'item' => 'Achievement_claim'
                )
            ));
        }

        return $missionList;
    }

    /**
     * 判断任务条件的进度已经达成
     *
     * @param int $userId
     * @param int $missionId
     * @param int $conditionId
     * @param MissionVo $mission
     * @param array $taskSetting
     * @param array $taskConditionSetting
     * @return boolean
     */
    private function isConditionSatisfyDemand($userId, $missionId, $conditionId, $mission = null, $taskSetting = null, $taskConditionSetting = null)
    {
        if (!$mission) {
            $missionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_MISSION);
            $mission = $missionList->getElement($missionId);
        }

        if (!$taskSetting) {
            $taskSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_TASK, $missionId, true);
        }

        if (!$taskConditionSetting) {
            $taskConditionSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_TASK_CONDITION, $conditionId, true);
        }

        // 判断成就类型的任务
        if ($taskSetting['taskType'] == GameConst::MISSION_TYPE_ACHIEVEMENT) {
            return false;
        }

        // 判断任务条件是否满足
        $conditionProgresses = $mission->getRawConditionsProgress();
        return !($taskConditionSetting['count'] > $conditionProgresses[$conditionId]);
    }


    /**
     * 判断任务的全部任务条件已达成
     *
     * @param int $userId
     * @param int $missionId
     * @param MissionVo $mission default null
     * @param array $taskConditionSettings default null
     * @param ModuleItemVoList $itemList default null
     * @return array
     * <pre>
     *  array(boolean, [conditionId,...], {itemDefId:count, ...})
     * </pre>
     */
    private function isSatisfyDemand($userId, $missionId, $mission = null, $taskConditionSettings = null, $itemList = null)
    {
        if (!$mission) {
            $missionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_MISSION);
            $mission = $missionList->getElement($missionId);
        }

        if (!$itemList) {
            $itemList = SzPersister::get()->getVoList($userId, ModuleItemConst::ORM_NAME_ITEM);
        }

        if (!$taskConditionSettings) {
            $taskConditionSettings = SzConfig::get()->loadAppConfig(GameConst::CONFIG_TASK_CONDITION, null, true);
        }

        // 任务条件验证
        $notSatisfyConditionIds = [];
        $consumeItems = [];
        foreach ($mission->getRawConditionsProgress() as $conditionId => $progressCount) {
            if (!SzUtility::checkArrayKey($conditionId, $taskConditionSettings)) {
                continue;
            }
            $taskConditionSetting = $taskConditionSettings[$conditionId];

            // 判断进度是否达到配置要求
            if ($taskConditionSetting['count'] > $progressCount) {
                // 任务不达标，则判断是否属于道具消耗
                list($action, $module, $subId) = EntityService::get()->parseEntityId($taskConditionSetting['content']);
                if ($action == GameConst::ENTITY_ACTION_CONSUME && $module == GameConst::ENTITY_TYPE_ITEM) {
                    // 当任务条件是消耗道具，则判断背包中道具数量是否满足条件
                    if ($taskConditionSetting['count'] > $itemList->getItemDefCount($subId)) {
                        $notSatisfyConditionIds[] = $conditionId;
                    } else {
                        if (SzUtility::checkArrayKey($subId, $consumeItems)) {
                            $consumeItems[$subId] += $taskConditionSetting['count'];
                        } else {
                            $consumeItems[$subId] = $taskConditionSetting['count'];
                        }
                    }
                } else {
                    $notSatisfyConditionIds[] = $conditionId;
                }
            }
        }

        return array(
            empty($notSatisfyConditionIds),
            $notSatisfyConditionIds,
            $consumeItems
        );
    }

    /**
     * 跳过该任务的条件(自动完成)
     *
     * @param $userId
     * @param $missionId
     * @param $conditionId
     * @param $reqTime
     * @return MissionVo
     * @throws SzException
     */
    public function skippingCondition($userId, $missionId, $conditionId, $reqTime)
    {
        $taskSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_TASK, $missionId, true);
        $taskConditionSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_TASK_CONDITION, $conditionId, true);

        /**
         * @var MissionVoList $missionList
         * @var MissionVo $mission
         */
        $missionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_MISSION);
        $mission = $missionList->getElement($missionId);

        if ($this->isConditionSatisfyDemand($userId, $missionId, $conditionId, $mission, $taskSetting, $taskConditionSetting)) {
            return $mission;
        }

        // 判断任务条件是否可以跳过
        if ($taskConditionSetting['gemForSkipping'] == -1) {
            throw new SzException(21110);
        }

        // 扣除任务跳过的花费
        GameService::get()->handleEntity($userId, GameConst::ENTITY_ACTION_CONSUME_MONEY, $taskConditionSetting['gemForSkipping'], true);

        // 更新任务条件
        $conditionsProgress = $mission->getRawConditionsProgress();
        $conditionsProgress[$conditionId] = $taskConditionSetting['count'];
        $mission->setConditionsProgress($conditionsProgress);
        $missionList->updateElement($missionId, $mission);
        SzPersister::get()->setVoList($missionList);

        // 数据采集:旅行屋任务条件跳过
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_USER_ORBIT, array(
            'clientData' => array(
                'tid' => 'Module:Travel|action:skip|',
                'item' => $missionId . "_" . "$conditionId",
                'value' => 1
            )
        ));

        // 数据采集:旅行屋跳过任务消耗钻石
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_USE_GOLD, array(
            'clientData' => array(
                'item' => 'Travel_skip_' . $missionId . '_' . $conditionId,
                'value' => abs($taskConditionSetting['gemForSkipping'])
            )
        ));

        return $mission;
    }

    /**
     * 获取任务信息
     *
     * @param int $userId
     * @param int $missionId
     * @param boolean $errNotFound default true
     * @return MissionVo
     */
    public function getMissionInfo($userId, $missionId, $errNotFound = true)
    {
        /**
         * @var MissionVoList $missionList
         * @var MissionVo $mission
         */
        $missionList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_MISSION);
        $mission = $missionList->getElement($missionId, $errNotFound);

        return $mission;
    }
}